﻿using System;
using System.Collections.Generic;
using System.Linq;
using CustomerContactSystem.Core.Infrastructure.Domain;
using CustomerContactSystem.Core.Infrastructure.Utility;

namespace CustomerContactSystem.Core.Model {
    public class Enquiry : Entity {
        public virtual int FixYourStreetId { get; set; }
        public virtual DateTime DateOfEnquiry { get; set; }
        public virtual EnquiryClassification Classification { get; set; }
        public virtual EnquiryCategory Category { get; set; }
        public virtual EnquirySource Source { get; set; }
        public virtual User SubmittedBy { get; set; }
        public virtual Department SubmittedByDepartment { get; set; }
        public virtual UserGroup SubmittedByGroup { get; set; }
        public virtual Customer Customer { get; set; }
        public virtual Rep Rep { get; set; }
        public virtual string EnquiryDetail { get; set; }
        public virtual DateTime Deadline { get; set; }
        public virtual bool IsMediaEnquiry { get; set; }
        public virtual bool IsConfidential { get; set; }
        public virtual string CurrentStatus { get; set; }
        public virtual double FirstResponseTime { get; set; }
        public virtual EnquiryFirstReferral FirstReferral { get; set; }
        public virtual EnquiryCurrentReferral CurrentReferral { get; set; }
        public virtual EnquiryClosure Closure { get; set; }
        public virtual EnquiryCommitmentClosure CommitmentClosure { get; set; }
        public virtual EnquiryDeletion Deletion { get; set; }
        private readonly ICollection<Tag> _tags;
        public virtual IEnumerable<Tag> Tags { get { return _tags.Select(x => x); } }
        private readonly ICollection<EnquiryAvoidable> _avoidables;
        public virtual IEnumerable<EnquiryAvoidable> Avoidables { get { return _avoidables.Select(x => x); } }
        private readonly ICollection<EnquiryComment> _comments;
        public virtual IEnumerable<EnquiryComment> Comments { get { return _comments.Select(x => x); } }
        private readonly ICollection<EnquiryDocument> _documents;
        public virtual IEnumerable<EnquiryDocument> Documents { get { return _documents.Select(x => x); } }
        private readonly ICollection<EnquiryReferral> _referrals;
        public virtual IEnumerable<EnquiryReferral> Referrals { get { return _referrals.Select(x => x); } } 
        private readonly ICollection<EnquiryHistory> _history;
        public virtual IEnumerable<EnquiryHistory> History { get { return _history.Select(x => x); } }
        private readonly ICollection<EnquiryFixYourStreetStatusUpdate> _fixYourStreetStatusUpdates;
        public virtual IEnumerable<EnquiryFixYourStreetStatusUpdate> FixYourStreetStatusUpdates { get { return _fixYourStreetStatusUpdates.Select(x => x); } }
        private readonly ICollection<EnquiryEmail> _emails;
        public virtual IEnumerable<EnquiryEmail> Emails { get { return _emails.Select(x => x); } }

        public Enquiry() {
            _tags = new HashSet<Tag>();
            _avoidables = new HashSet<EnquiryAvoidable>();
            _comments = new HashSet<EnquiryComment>();
            _documents = new HashSet<EnquiryDocument>();
            _referrals = new HashSet<EnquiryReferral>();
            _history = new HashSet<EnquiryHistory>();
            _fixYourStreetStatusUpdates = new HashSet<EnquiryFixYourStreetStatusUpdate>();
            _emails = new HashSet<EnquiryEmail>();
        }

        public virtual void AddFixYourStreetStatusUpdate(string status) {
            if (FixYourStreetId == 0) {
                return;
            }

            _fixYourStreetStatusUpdates.Add(new EnquiryFixYourStreetStatusUpdate
                {
                    Enquiry = this,
                    DateCreated = DateTime.Now,
                    FixYourStreetId = FixYourStreetId,
                    Status = status,
                    StatusSent = false
                });
        }

        public virtual void AddTag(string username, Tag tag)
        {
            if (_tags.Contains(tag)) {
                return;
            }
            _tags.Add(tag);

            _history.Add(new EnquiryHistory
                {
                    EventDate = DateTime.Now,
                    EventDetail = "Tagged with " + tag.TagName + " by " + username
                });
        }

        public virtual void RemoveTag(string username, Tag tag)
        {
            _tags.Remove(tag);

            _history.Add(new EnquiryHistory
            {
                EventDate = DateTime.Now,
                EventDetail = "Tag, " + tag.TagName + ", removed by " + username
            });
        }

        public virtual void AddAvoidable(EnquiryAvoidable avoidable)
        {
            if (_avoidables.Contains(avoidable)) {
                return;
            }
            avoidable.Enquiry = this;
            _avoidables.Add(avoidable);
        }

        public virtual void RemoveAvoidable(EnquiryAvoidable avoidable)
        {
            avoidable.Enquiry = null;
            _avoidables.Remove(avoidable);
        }

        public virtual void ChangeCategory(EnquiryCategory category) {
            if (Category == category) {
                return;
            }

            var previousCategoryName = Category.CategoryName;
            var previousDepartmentName = Category.Department.DepartmentName;

            Category = category;

            _history.Add(new EnquiryHistory
                {
                    Enquiry = this,
                    EventDate = DateTime.Now,
                    EventDetail = "Category updated from " + previousCategoryName + " (" + previousDepartmentName + ") to " +
                                  Category.CategoryName + " (" + Category.Department.DepartmentName + ")"
                });
        }

        public virtual EnquiryEmail EmailCustomer(string emailContent, string sentFrom, string sentTo, string subject, string username) {
            var customerEmail = new EnquiryEmail
                {
                    Attachments = "",
                    BccList = "",
                    CcList = "",
                    DateCreated = DateTime.Now,
                    EmailBody = emailContent,
                    Enquiry = this,
                    HasBeenSent = false,
                    IsHtml = true,
                    SentFrom = sentFrom,
                    SentTo = sentTo,
                    Subject = subject
                };
            _emails.Add(customerEmail);

            _history.Add(new EnquiryHistory
                {
                    Enquiry = this,
                    EventDate = customerEmail.DateCreated,
                    EventDetail = "Email sent to customer by " + username + " - see comment for detail"
                });

            return customerEmail;
        }

        public virtual EnquiryComment AddComment(User addedBy, string commentText, bool sendToFixYourStreet, bool completesCommitment, bool notifySubmitter, User otherUser, string viewUrl) {
            var comment = new EnquiryComment
                {
                   AddedBy = addedBy,
                   Comment = commentText.HtmlStripped().Replace("/n", "<br />"),
                   CommentDate = DateTime.Now,
                   Enquiry = this,
                   FixYourStreetCommentId = 0,
                   SentToFixYourStreet = sendToFixYourStreet
                };

            _comments.Add(comment);

            if (FixYourStreetId > 0 && sendToFixYourStreet && FirstResponseTime <= 0) {
                FirstResponseTime = comment.CommentDate.Subtract(DateOfEnquiry).TotalHours;
                _fixYourStreetStatusUpdates.Add(new EnquiryFixYourStreetStatusUpdate
                {
                    DateCreated = DateTime.Now.AddMinutes(-1),
                    Enquiry = this,
                    FixYourStreetId = FixYourStreetId,
                    Status = "P",
                    StatusSent = false
                });
            }

            var commitmentCompletion = "";
            if (CommitmentClosure != null) {
                if (completesCommitment) {
                    CommitmentClosure.Completed = true;
                    commitmentCompletion = " completing Commitment";

                    _fixYourStreetStatusUpdates.Add(new EnquiryFixYourStreetStatusUpdate
                        {
                            DateCreated = DateTime.Now,
                            Enquiry = this,
                            FixYourStreetId = FixYourStreetId,
                            Status = "C",
                            StatusSent = false
                        });
                }
            }

            _history.Add(new EnquiryHistory
            {
                Enquiry = this,
                EventDate = comment.CommentDate,
                EventDetail = "Comment added by " + addedBy.DisplayName + commitmentCompletion
            });

            if (notifySubmitter) {
                var notifyEmail = EnquiryEmail.CommentAddedEmail(addedBy.Email, SubmittedBy.Email, addedBy.DisplayName,
                                                                 EnquiryDetail, comment.Comment, viewUrl);
                notifyEmail.Enquiry = this;
                _emails.Add(notifyEmail);
            }

            if (otherUser != null) {
                var notifyEmail = EnquiryEmail.CommentAddedEmail(addedBy.Email, otherUser.Email, addedBy.DisplayName,
                                                                 EnquiryDetail, comment.Comment, viewUrl);
                notifyEmail.Enquiry = this;
                _emails.Add(notifyEmail);
            }

            return comment;
        }

        public virtual EnquiryComment UpdateComment(string username, int commentId, string commentText, bool sendToFixYourStreet, bool completesCommitment) {
            var existingComment = _comments.SingleOrDefault(c => c.Id == commentId);

            if (existingComment != null) {
                existingComment.Comment = commentText.HtmlStripped().Replace("/n", "<br />");
                existingComment.SentToFixYourStreet = sendToFixYourStreet;

                var commitmentCompletion = "";
                if (CommitmentClosure != null) {
                    if (completesCommitment) {
                        CommitmentClosure.Completed = true;
                        commitmentCompletion = " completing Commitment";
                    }
                }

                _history.Add(new EnquiryHistory
                {
                    Enquiry = this,
                    EventDate = DateTime.Now,
                    EventDetail = "Comment updated by " + username + commitmentCompletion
                });
            }

            return existingComment;
        }

        public virtual void RemoveComment(string username, int commentId) {
            var existingComment = _comments.SingleOrDefault(c => c.Id == commentId);
            if (existingComment == null) {
                return;
            }
            _comments.Remove(existingComment);

            _history.Add(new EnquiryHistory
            {
                Enquiry = this,
                EventDate = DateTime.Now,
                EventDetail = "Comment deleted by " + username
            });
        }

        public virtual EnquiryDocument AddDocument(User addedBy, string documentName, string documentDescription, bool notifySubmitter, User otherUser, string viewUrl, string documentRoot, string documentType) {
            var document = new EnquiryDocument
                {
                    Enquiry = this,
                    AddedBy = addedBy,
                    DateAdded = DateTime.Now,
                    DocumentDescription = documentDescription,
                    DocumentName = documentName,
                    DocumentType = documentType,
                    FileName = Guid.NewGuid() + "." + documentType
                };
            _documents.Add(document);

            _history.Add(new EnquiryHistory
            {
                Enquiry = this,
                EventDate = document.DateAdded,
                EventDetail = "Document, " + document.DocumentName + ", added by " + addedBy.DisplayName
            });

            if (notifySubmitter) {
                var notifyEmail = EnquiryEmail.DocumentAddedEmail(addedBy.Email, SubmittedBy.Email, addedBy.DisplayName,
                                                                  EnquiryDetail,
                                                                  documentRoot + document.SavePath() + document.FileName,
                                                                  viewUrl);
                notifyEmail.Enquiry = this;
                _emails.Add(notifyEmail);
            }

            if (otherUser != null) {
                var notifyEmail = EnquiryEmail.DocumentAddedEmail(addedBy.Email, otherUser.Email, addedBy.DisplayName,
                                                                  EnquiryDetail,
                                                                  documentRoot + document.SavePath() + document.FileName,
                                                                  viewUrl);
                notifyEmail.Enquiry = this;
                _emails.Add(notifyEmail);
            }

            return document;
        }

        public virtual EnquiryDocument UpdateDocument(string username, int documentId, string documentName, string documentDescription) {
            var document = _documents.SingleOrDefault(d => d.Id == documentId);

            if (document != null) {
                document.DocumentName = documentName;
                document.DocumentDescription = documentDescription;

                _history.Add(new EnquiryHistory
                {
                    Enquiry = this,
                    EventDate = DateTime.Now,
                    EventDetail = "Document updated by " + username
                });
            }

            return document;
        }

        public virtual void RemoveDocument(string username, int documentId) {
            var document = _documents.SingleOrDefault(d => d.Id == documentId);

            if (document == null) {
                return;
            }
            
            _documents.Remove(document);

            _history.Add(new EnquiryHistory
            {
                Enquiry = this,
                EventDate = document.DateAdded,
                EventDetail = "Document, " + document.DocumentName + ", deleted by " + username
            });
        }

        public virtual EnquiryReferral Refer(User referredBy, User referredTo, string viewUrl) {
            var referral = new EnquiryReferral
                {
                    Enquiry = this,
                    DateReferred = DateTime.Now,
                    ReferredTo = referredTo,
                    ReferredToDepartment = referredTo.Department,
                    ReferredToGroup = referredTo.UserGroup                    
                };
            _referrals.Add(referral);

            CurrentStatus = "Referred to " + referredTo.DisplayName + " by " + referredBy.DisplayName;

            _history.Add(new EnquiryHistory
            {
                Enquiry = this,
                EventDate = referral.DateReferred,
                EventDetail = CurrentStatus
            });

            var customerInfo = "Anonymous";
            if (Customer != null) {
                customerInfo = Customer.FullName + ", " + Customer.AddressLine;
            }
            var referralEmail = EnquiryEmail.ReferralEmail(referredBy.Email, referredTo.Email, viewUrl,
                                                           referredBy.DisplayName, customerInfo, EnquiryDetail, Deadline);
            referralEmail.Enquiry = this;
            _emails.Add(referralEmail);

            return referral;
        }

        public virtual void Close(string username, EnquiryClosure closure, bool addStatus) {
            Closure = closure;

            if (FixYourStreetId > 0) {
                AddComment(closure.ClosedBy, closure.EnquiryResponse, true, false, false, null, "");
            }

            if (addStatus && FixYourStreetId > 0 && CommitmentClosure == null) {
                var fixYourStreetStatus = new EnquiryFixYourStreetStatusUpdate
                {
                    Enquiry = this,
                    DateCreated = closure.DateClosed,
                    FixYourStreetId = FixYourStreetId,
                    Status = "C",
                    StatusSent = false
                };

                _fixYourStreetStatusUpdates.Add(fixYourStreetStatus);
            }

            if (FixYourStreetId > 0 && FirstResponseTime <= 0) {
                FirstResponseTime = closure.DateClosed.Subtract(DateOfEnquiry).TotalHours;                
            }

            CurrentStatus = "Closed by " + username;
            _history.Add(new EnquiryHistory
            {
                Enquiry = this,
                EventDate = closure.DateClosed,
                EventDetail = CurrentStatus

            });
        }

        public virtual void AddCommitment(string username, EnquiryCommitmentClosure commitmentClosure, DateTime dateAdded) {
            CommitmentClosure = commitmentClosure;

            if (FixYourStreetId > 0) {
                var fixYourStreetStatus = new EnquiryFixYourStreetStatusUpdate
                {
                    Enquiry = this,
                    DateCreated = dateAdded,
                    FixYourStreetId = FixYourStreetId,
                    Status = "M",
                    StatusSent = false
                };

                _fixYourStreetStatusUpdates.Add(fixYourStreetStatus);
            }

            _history.Add(new EnquiryHistory
            {
                Enquiry = this,
                EventDate = dateAdded,
                EventDetail = "Commitment added by " + username

            });
        }

        public virtual void AddHistory(DateTime eventDate, string eventText) {
            var history = new EnquiryHistory
                {
                    Enquiry = this,
                    EventDate = eventDate,
                    EventDetail = eventText
                };

            if (!_history.Contains(history)) {
                _history.Add(history);
            }
        }

        public virtual void Delete(string username, EnquiryDeletion deletion) {
            Deletion = deletion;
            _history.Add(new EnquiryHistory
                {
                    EventDate = deletion.DateOfDeletion,
                    EventDetail = "Deleted by " + username
                });
        }

        public virtual void SetAsAnonymous(string username) {
            var customerInfo = "Anonymous";
            if (Customer != null) {
                customerInfo = Customer.FullName + ", " + Customer.AddressLine;
            }

            Customer = null;

            _history.Add(new EnquiryHistory
                {
                    Enquiry = this,
                    EventDate = DateTime.Now,
                    EventDetail = "Customer (" + customerInfo + ") removed and Set as anonymous by " + username
                });
        }

        public virtual void ChangeCustomer(string username, Customer customer) {
            var customerInfo = "Anonymous";
            if (Customer != null) {
                customerInfo = Customer.FullName + ", " + Customer.AddressLine;
            }

            Customer = customer;

            _history.Add(new EnquiryHistory
                {
                    Enquiry = this,
                    EventDate = DateTime.Now,
                    EventDetail =
                        "Customer changed from " + customerInfo + " to " + Customer.FullName + ", " +
                        Customer.AddressLine + " by " + username
                });
        }

        protected override void Validate() {
            if (DateOfEnquiry == DateTime.MinValue) {
                AddBrokenRule(new BusinessRule("DateOfEnquiry", "Date of enquiry is required!"));
            }

            if (Classification == null || Classification.Id == 0) {
                AddBrokenRule(new BusinessRule("Classification", "Classification is required!"));
            }

            if (Category == null || Category.Id == 0) {
                AddBrokenRule(new BusinessRule("Category", "Category is required!"));
            }

            if (Source == null || Source.Id == 0) {
                AddBrokenRule(new BusinessRule("Source", "Source is required!"));
            }

            if (SubmittedBy == null || SubmittedBy.Id == 0) {
                AddBrokenRule(new BusinessRule("SubmittedBy", "Submitted by is required!"));
            }

            if (SubmittedByDepartment == null || SubmittedByDepartment.Id == 0)
            {
                AddBrokenRule(new BusinessRule("SubmittedByDepartment", "Submitted by department is required!"));
            }

            if (SubmittedByGroup == null || SubmittedByGroup.Id == 0)
            {
                AddBrokenRule(new BusinessRule("SubmittedByGroup", "Submitted by group is required!"));
            }

            if (string.IsNullOrEmpty(EnquiryDetail)) {
                AddBrokenRule(new BusinessRule("EnquiryDetail", "Enquiry detail is required!"));
            }

            if (Deadline == DateTime.MinValue) {
                AddBrokenRule(new BusinessRule("Deadline", "Deadline is required!"));
            }

            if (string.IsNullOrEmpty(CurrentStatus)) {
                AddBrokenRule(new BusinessRule("CurrentStatus", "Current status is required!"));
            }
        }
    }
}